package gdufs.lkl.calculator;

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * 表达式计算器类，用于计算带有四则运算和括号的数学表达式
 */
public class ExpressionEvaluator {

    /**
     * 计算数学表达式的值
     * @param expr 数学表达式字符串，如"2*(3+4)"
     * @return 计算结果
     * @throws Exception 计算错误时抛出异常
     */
    public static double evaluate(String expr) throws Exception {
        expr = preprocessExpression(expr);
        
        // 使用双栈法计算表达式
        Deque<Double> numbers = new ArrayDeque<>();
        Deque<Character> operators = new ArrayDeque<>();

        for (int i = 0; i < expr.length(); i++) {
            char c = expr.charAt(i);

            if (Character.isDigit(c) || c == '.') {
                StringBuilder num = new StringBuilder();
                while (i < expr.length() &&
                        (Character.isDigit(expr.charAt(i)) || expr.charAt(i) == '.')) {
                    num.append(expr.charAt(i));
                    i++;
                }
                i--;
                numbers.push(Double.parseDouble(num.toString()));
            } else if (c == '(') {
                operators.push(c);
            } else if (c == ')') {
                while (!operators.isEmpty() && operators.peek() != '(') {
                    numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
                }
                if (!operators.isEmpty() && operators.peek() == '(') {
                    operators.pop(); // 弹出 '('
                }
            } else if (isOperator(c)) {
                while (!operators.isEmpty() && hasPrecedence(c, operators.peek())) {
                    numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
                }
                operators.push(c);
            }
        }

        while (!operators.isEmpty()) {
            numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
        }

        if (numbers.isEmpty()) {
            throw new Exception("表达式错误");
        }
        
        return numbers.pop();
    }
    
    // 预处理表达式，将单目负号转换为 "0-"
    private static String preprocessExpression(String expr) {
        // 处理表达式开头的负号
        if (expr.startsWith("-")) {
            expr = "0" + expr;
        }
        
        // 处理括号后的负号
        expr = expr.replaceAll("\\(-", "(0-");
        
        // 处理运算符后的负号
        expr = expr.replaceAll("\\+-", "+0-");
        expr = expr.replaceAll("--", "-0-");
        expr = expr.replaceAll("\\*-", "*0-");
        expr = expr.replaceAll("/-", "/0-");
        
        // 将 × 和 ÷ 替换为 * 和 /
        expr = expr.replace("×", "*");
        expr = expr.replace("÷", "/");
        
        return expr;
    }

    // 应用运算符计算
    private static double applyOperation(char op, double b, double a) throws Exception {
        switch (op) {
            case '+': return a + b;
            case '-': return a - b;
            case '*': return a * b;
            case '/':
                if (b == 0) throw new ArithmeticException("除以零");
                return a / b;
            default:
                throw new Exception("未知运算符: " + op);
        }
    }

    // 检查运算符优先级
    private static boolean hasPrecedence(char op1, char op2) {
        if (op2 == '(' || op2 == ')') return false;
        return (op1 != '*' && op1 != '/') || (op2 != '+' && op2 != '-');
    }

    // 检查字符是否为运算符
    private static boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }
}
